package gamePlus.client;

import java.awt.EventQueue;
import java.util.LinkedList;

import protocol.Demand;
import protocol.SinglePlayerMove;
import protocol.WaitTimeChange;

import GUI.gameWin.ClientGameWindow;
import GUI.gameWin.GameButtonPanel;
import GUI.gameWin.GameButtonPanel.GBState;

import game.Player;
import gamePlus.server.GameRules;
import carddeck.Card;
import carddeck.Rank;
import carddeck.Suit;
import carddeckPlus.CardDeckPlus;

public class ClientRoundUnit {
	
	//game related
	public CardDeckPlus usedCardDeck;
	public Card cardRevealed=null;
	public int freshCardCount=0;
	public int waitPenalty=0;
	public int cardPenalty=0;
	public Rank demand=null;
	public Player demandedBy=null;
	public LinkedList<Player> players;
	public Player currentPlayer=null;
	
	//client related
	public LinkedList<Card> selection;
	public LinkedList<Card> oldSelection;
	public boolean drawSelected = false;
	public Player me;
	
	//gui related
	public boolean animationPlays;
	
	public ClientRoundUnit(String[] playerNames, String myName) {
		players = new LinkedList<>();
		for(String name: playerNames) {
			Player player = new Player(name);
			players.add(player);
			if(name.equals(myName))
				me=player;
		}
		usedCardDeck = new CardDeckPlus(0);
		oldSelection = new LinkedList<>();
		selection = new LinkedList<>();
		animationPlays = true;
	}
	
	public void usedRefreshed() {
		if(usedCardDeck.getCardCount()==0)
			return;
		Card lastCard = usedCardDeck.getLastCard();
		usedCardDeck.reset();
		usedCardDeck.addCard(lastCard);
		ClientGameWindow.getInstance().getGamePanel().getUsedPanel().refresh();
	}

	public void drawCardActionPerformed() {
		if(animationPlays||currentPlayer!=me)
			return;
		if(!selection.isEmpty()) {
			for(Card theCard: selection) {
				theCard.setHighlighted(false);
				theCard.getButton().setHighlighted(false);
			}
			selection.clear();
		}
		if(drawSelected) {
			drawSelected = false;
			ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(false);
			ClientGameWindow.getInstance().setButtonState(GBState.None);
		}
		else {
			drawSelected = true;
			ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(true);
			if(cardPenalty==0)
				ClientGameWindow.getInstance().setButtonState(GBState.GB_DRAW_CARD);
			else
				ClientGameWindow.getInstance().setButtonState(GBState.GB_DRAW_CARDS);
		}
	}

	public void selectCardActionPerformed(Card card,
			boolean b) {
		if(animationPlays||card.getButton().getOwner()!=me||currentPlayer!=me&&currentPlayer!=null)
			return;
		if(card.isJoker()&&card.getFakeCard()==null) {
			ClientGameWindow.getInstance().requestJokerSelection(card);
			return;
		}
		if(selection.isEmpty()) {
			if(GameRules.canPlayOn(card.getRealCard(), usedCardDeck.peekLastCard().getRealCard(), cardPenalty, waitPenalty, demand)) {
				selection.add(card);
				card.setHighlighted(true);
				card.getButton().setHighlighted(true);
				if(drawSelected) {
					drawSelected = false;
					ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(false);
				}
				ClientGameWindow.getInstance().setButtonState(GBState.GB_PLAY_CARD);
			}
			else {
				card.setFakeCard(null); // clear joker
			}
		}
		else {
			boolean deselect=false;
			for(Card theCard: selection)
				if(theCard==card) {
					card.setHighlighted(false);
					card.getButton().setHighlighted(false);
					card.setFakeCard(null);
					deselect=true;
				}
			if(selection.getFirst()==card&&(selection.size()==1||!GameRules.canPlayOn(selection.get(1), usedCardDeck.peekLastCard().getRealCard(), cardPenalty, waitPenalty, demand))) {
				for(Card theCard: selection) {
					theCard.setHighlighted(false);
					theCard.getButton().setHighlighted(false);
					theCard.setFakeCard(null);
				}
				selection.clear();
				if(cardRevealed!=null)
					ClientGameWindow.getInstance().setButtonState(GBState.GB_DRAW_CARD);
				else if(currentPlayer==null)
					ClientGameWindow.getInstance().setButtonState(GBState.GB_INITIAL_NONE);
				else if(waitPenalty>0)
					ClientGameWindow.getInstance().setButtonState(GBState.GB_WAIT);
				else
					ClientGameWindow.getInstance().setButtonState(GBState.None);
			}
			else {
				if(deselect) {
					selection.remove(card);
					if(selection.size()==1)
						ClientGameWindow.getInstance().setButtonState(GBState.GB_PLAY_CARD);
				}
				else {
					if(GameRules.canPlayWith(card.getRealCard(), selection.getFirst().getRealCard(), cardPenalty, waitPenalty)) {
						selection.add(card);
						card.setHighlighted(true);
						card.getButton().setHighlighted(true);
						ClientGameWindow.getInstance().setButtonState(GBState.GB_PLAY_CARDS);
					}
					else {
						card.setFakeCard(null); // clear joker
					}
				}
			}
		}
		
	}

	public void initialGiveOut(LinkedList<Card> cards) {
		int i = 0;
		animationPlays = true;
		for(Card theCard: cards) {
			players.get(i%players.size()).addCard(theCard);
			i++;
			updateFreshCount(freshCardCount-1);
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void postGiveOut(Card theCard) {
		ClientGameWindow.getInstance().setButtonState(GBState.None);
		animationPlays = true;
		usedCardDeck.addCard(theCard);
		updateFreshCount(freshCardCount-1);
		ClientGameWindow.getInstance().getGamePanel().getUsedPanel().refresh();
		if(theCard.bIsNeutral()) {
			animationPlays=false;
			ClientGameWindow.getInstance().setButtonState(GBState.GB_INITIAL_NONE);
		}
	}

	public void updateFreshCount(int count) {
		freshCardCount = count;
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().refresh();
	}

	public void updateCardPenalty(int penalty) {
		cardPenalty = penalty;
		ClientGameWindow.getInstance().getGamePanel().getPenaltyPanel().updateCardPenalty(penalty);
	}
	
	public void updateWaitPenalty(int penalty) {
		waitPenalty = penalty;
		ClientGameWindow.getInstance().getGamePanel().getPenaltyPanel().updateWaitPenalty(penalty);
	}

	public void nextPlayer(String name) {
		for(Player player: players)
			if(player.getName().equals(name)) {
				if(currentPlayer!=null)
					currentPlayer.deselect();
				currentPlayer=player;
				currentPlayer.select();
			}
		ClientGameWindow.getInstance().setButtonState(GBState.None);
		if(currentPlayer==me&&waitPenalty>0)
			ClientGameWindow.getInstance().setButtonState(GBState.GB_WAIT);
	}

	public void colorSet(Suit color) {
		usedCardDeck.peekLastCard().setFakeCard(new Card(color, Rank.ACE));
		ClientGameWindow.getInstance().getGamePanel().getUsedPanel().refresh();
	}

	public void demandStarted(Demand demand) {
		ClientGameWindow.getInstance().getGamePanel().setDemand(demand);
	}

	public void clearDemand() {
		ClientGameWindow.getInstance().getGamePanel().setDemand(null);
	}

	public void setWaitTime(WaitTimeChange wtc) {
		for(Player player: players)
			if(player.getName().equals(wtc.playerName))
				player.setWaitTime(wtc.waitTime);
	}

	public void playerAfterdrawn() {
		clearOldSelection();
		if(cardRevealed==null)
			throw new UnsupportedOperationException("cardRevealed can NOT be null!");
		currentPlayer.addCard(cardRevealed);
		oldSelection.add(cardRevealed);
		cardRevealed.setHighlighted(true);
		cardRevealed.getButton().setHighlighted(true);
		cardRevealed=null;
		freshCardCount--;
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(false);
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().disableAfterdraw();
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().refresh();
	}

	public void playerPeekCard(Card theCard) {
		cardRevealed = theCard;
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().switchToAfterdraw(currentPlayer, theCard);
		if(currentPlayer==me) {
			EventQueue.invokeLater(new Runnable() {
				@Override
				public void run() {
					ClientGameWindow.getInstance().setButtonState(GBState.GB_DRAW_CARD);
				}
			});
		}
	}

	public void cardsPlayed(SinglePlayerMove singlePlayerMove) {
		animationPlays=true;
		if(currentPlayer==null) {
			for(Card theCard: selection) {
				theCard.setHighlighted(false);
				theCard.getButton().setHighlighted(false);
			}
			selection.clear();
			ClientGameWindow.getInstance().setButtonState(GBState.None);
		}
		clearOldSelection();
		Player playerPlaying = null;
		for(Player player: players) 
			if(player.getName().equals(singlePlayerMove.playerName))
				playerPlaying=player;
		LinkedList<Card> cardsToRemove = new LinkedList<>();
		if(playerPlaying!=me) {
			if(cardRevealed!=null) {
				cardRevealed.setHighlighted(true);
				cardRevealed.getButton().setHighlighted(true);
				cardRevealed.setFakeCard(singlePlayerMove.cardsToPlay.getFirst().getFakeCard());
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			for(Card theCard: singlePlayerMove.cardsToPlay) {
				if(cardRevealed!=null&&theCard==singlePlayerMove.cardsToPlay.getFirst())
					continue;
				for(Card playerCard: playerPlaying.getCards())
					if(playerCard.isEqualTo(theCard)&&!cardsToRemove.contains(playerCard)) {
						playerCard.setHighlighted(true);
						playerCard.getButton().setHighlighted(true);
						playerCard.setFakeCard(theCard.getFakeCard());
						cardsToRemove.add(playerCard);
						break;
					}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}//dodatkowa, druga sekunda
		}
		else {
			for(Card theCard: singlePlayerMove.cardsToPlay) {
				if(cardRevealed!=null&&theCard==singlePlayerMove.cardsToPlay.getFirst())
					continue;
				for(Card playerCard: playerPlaying.getCards())
					if(playerCard.isEqualTo(theCard)&&!cardsToRemove.contains(playerCard)) {
						playerCard.setFakeCard(theCard.getFakeCard());
						cardsToRemove.add(playerCard);
						break;
					}
			}
		}
		//deselect and drop all
		if(cardRevealed!=null) {
			cardRevealed.setHighlighted(false);
			cardRevealed.getButton().setHighlighted(false);
			cardRevealed.setFakeCard(singlePlayerMove.cardsToPlay.getFirst().getFakeCard());
			usedCardDeck.addCard(cardRevealed);
			cardRevealed=null;
			freshCardCount--;
			ClientGameWindow.getInstance().getGamePanel().getFreshPanel().disableAfterdraw();
			ClientGameWindow.getInstance().getGamePanel().getFreshPanel().refresh();
			drawSelected=false;
			ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(false);
			ClientGameWindow.getInstance().getGamePanel().getUsedPanel().refresh();
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		for(Card playerCard: playerPlaying.getCards()) {
			playerCard.setHighlighted(false);
			playerCard.getButton().setHighlighted(false);
		}
		for(Card theCard:cardsToRemove)
		{
			playerPlaying.removeCard(theCard);
			usedCardDeck.addCard(theCard);
			ClientGameWindow.getInstance().getGamePanel().getUsedPanel().refresh();
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		animationPlays=false;
	}

	private void clearOldSelection() {
		for(Card theCard: oldSelection) {
			theCard.setHighlighted(false);
			theCard.getButton().setHighlighted(false);
		}
		oldSelection.clear();
	}
	
	public void drawnCards(LinkedList<Card> cards) {
		animationPlays = true;
		clearOldSelection();
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(true);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		for(Card theCard: cards) {
			currentPlayer.addCard(theCard);
			oldSelection.add(theCard);
			for(Card oldCard: oldSelection) {
				oldCard.setHighlighted(true);
				oldCard.getButton().setHighlighted(true);
			}
			updateFreshCount(freshCardCount-1);
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		ClientGameWindow.getInstance().getGamePanel().getFreshPanel().setHighlight(false);
		cardPenalty = 0;
		ClientGameWindow.getInstance().getGamePanel().getPenaltyPanel().updateCardPenalty(cardPenalty);
		animationPlays = false;
	}

	public void actionButtonPressed(GameButtonPanel.GBState buttonState) {
		if(animationPlays)
			return;
		drawSelected=false;
		ClientGameWindow.getInstance().setButtonState(GBState.None);
		switch(buttonState) {
		case GB_INITIAL_NONE:
			ClientGameWindow.getInstance().getClient().skippedInitial();
			break;
		case GB_PLAY_CARD:
		case GB_PLAY_CARDS:
			if(currentPlayer==null)
				ClientGameWindow.getInstance().getClient().initialPlayCards(selection);
			else
				ClientGameWindow.getInstance().getClient().playCards(selection);
			selection.clear();
			break;
		case GB_DRAW_CARD:
		case GB_DRAW_CARDS:
			if(cardRevealed!=null)
				ClientGameWindow.getInstance().getClient().afterdraw();
			else
				ClientGameWindow.getInstance().getClient().drawCards();
			break;
		case GB_WAIT:
			ClientGameWindow.getInstance().getClient().playerWait();
			break;
		}
	}
}
